//
// This code is to be used for evaluation of the CS2J tool ONLY.
//
// This file was translated from C# to Java by CS2J (http://www.cs2j.com).
//
// For more information about CS2J please contact info@twigletsoftware.com
//
// Translated by CS2J (http://www.cs2j.com): 2013/1/12 14:27:24
//

package ms.sudoku.technique;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import ms.sudoku.PuzzleState;
import ms.sudoku.util.IntSet;

public abstract class NakedSubsetTechnique extends EliminationTechnique {
	private int _subsetSize;
	private int[] _foundLocations;

	protected NakedSubsetTechnique(int subsetSize) {
		if (subsetSize < 2)
			throw new RuntimeException("subsetSize");

		_subsetSize = subsetSize;
		_foundLocations = new int[_subsetSize];
	}

	// optimization, rather than doing it in each call to EliminateNakedSubsets
	private int eliminateNakedSubsets(IntSet[] possibleNumbers, boolean exitEarlyWhenSoleFound,
			AtomicBoolean exitedEarly) {
		Integer changesMade = 0;
		exitedEarly.set(false);
		int[] foundLocations = _foundLocations;
		for (int i = 0; i < possibleNumbers.length; i++) {
			// optimization, rather than allocating each time
			if (possibleNumbers[i].size() == _subsetSize) {
				foundLocations[0] = i;
				int matchesFound = 0;
				for (int j = i + 1; j < possibleNumbers.length; j++) {
					if (possibleNumbers[j].size() == _subsetSize) {
						boolean foundMatch = possibleNumbers[i].equals(possibleNumbers[j]);
						if (foundMatch) {
							foundLocations[++matchesFound] = j;
							if (matchesFound == _subsetSize - 1) {
								for (int k = 0; k < possibleNumbers.length; k++) {
									if (Arrays.binarySearch(foundLocations, k) < 0) {
										for (int eliminatedPossible : possibleNumbers[i]) {
											if (possibleNumbers[k].contains(eliminatedPossible)) {
												changesMade++;
												possibleNumbers[k].remove(eliminatedPossible);
											}

										}
										if (exitEarlyWhenSoleFound && possibleNumbers[k].size() == 1) {
											exitedEarly.set(true);
											return changesMade;
										}

									}

								}
								break;
							}

						}

					}

				}
			}

		}
		return changesMade;
	}

	public boolean execute(PuzzleState state, boolean exitEarlyWhenSoleFound, IntSet[][] possibleNumbers,
			AtomicInteger numberOfChanges, AtomicBoolean exitedEarly) {
		numberOfChanges.set(0);
		exitedEarly.set(false);
		IntSet[] arrays = new IntSet[state.getGridSize()];
		for (Integer i = 0; i < state.getGridSize(); i++) {
			getRowPossibleNumbers(possibleNumbers, i, arrays);
			AtomicBoolean rowExitedEarly = new AtomicBoolean();
			numberOfChanges.addAndGet(eliminateNakedSubsets(arrays, exitEarlyWhenSoleFound, rowExitedEarly));
			exitedEarly.set(rowExitedEarly.get());
			if (exitedEarly.get())
				return false;

			getColumnPossibleNumbers(possibleNumbers, i, arrays);
			AtomicBoolean colExitedEarly = new AtomicBoolean();
			numberOfChanges.addAndGet(eliminateNakedSubsets(arrays, exitEarlyWhenSoleFound, colExitedEarly));
			exitedEarly.set(colExitedEarly.get());
			if (exitedEarly.get())
				return false;

			getBoxPossibleNumbers(state, possibleNumbers, i, arrays);
			AtomicBoolean boxExitedEarly = new AtomicBoolean();
			numberOfChanges.addAndGet(eliminateNakedSubsets(arrays, exitEarlyWhenSoleFound, boxExitedEarly));
			exitedEarly.set(boxExitedEarly.get());
			if (exitedEarly.get())
				return false;

		}
		return numberOfChanges.get() != 0;
	}

}
